package com.yf.esms.service;

import com.alibaba.fastjson.JSON;
import com.yf.esms.model.entries.User;
import com.yf.esms.model.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.DocWriteResponse;
import org.elasticsearch.action.bulk.BulkItemResponse;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author 99698
 */
@Service
@Slf4j
public class DocService {

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    /**
     * 新增单挑文档
     * @param insertUserVo
     * @return
     * @throws IOException
     */
    public DocCommonVo insertDoc(InsertUserVo insertUserVo) throws IOException {
        IndexRequest indexRequest = new IndexRequest();
        indexRequest
                //指定索引
                .index(insertUserVo.getIndexName())
                //指定文档id(user索引中使用用户id作为文档id)
                .id(String.valueOf(insertUserVo.getUser().getUserId()))
                //json字符串类型的数据
                .source(JSON.toJSONString(insertUserVo.getUser()), XContentType.JSON);
        IndexResponse indexResponse = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
        DocWriteResponse.Result result = indexResponse.getResult();
        String id = indexResponse.getId();
        DocCommonVo docCommonVo = new DocCommonVo();
        docCommonVo.setId(id);
        docCommonVo.setResult(result);
        return docCommonVo;
    }

    /**
     * 局部更新某一条文档的某些字段
     * @param updateUserVo
     * @return
     * @throws IOException
     */
    public DocWriteResponse.Result updateDoc(UpdateUserVo updateUserVo) throws IOException {
        UpdateRequest updateRequest = new UpdateRequest();
        if (StringUtils.isEmpty(updateUserVo.getId())){
            updateUserVo.setId(String.valueOf(updateUserVo.getUser().getUserId()));
        }
        updateRequest
                //索引
                .index(updateUserVo.getIndexName())
                //文档id
                .id(updateUserVo.getId())
                //要修改的field
                .doc(XContentType.JSON,
                        "age",updateUserVo.getUser().getAge(),
                        "userName",updateUserVo.getUser().getUserName(),
                        "mobile",updateUserVo.getUser().getMobile());

        UpdateResponse updateResponse = restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);
        DocWriteResponse.Result result = updateResponse.getResult();

        return result;
    }

    /**
     * 根据文档id获取该条文档的信息
     * @param selectUserVo
     * @return
     * @throws IOException
     */
    public User selectDoc(SelectUserVo selectUserVo) throws IOException {
        GetRequest getRequest = new GetRequest();
        getRequest.index(selectUserVo.getIndexName()).id(selectUserVo.getId());
        GetResponse getResponse = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
        String sourceAsString = getResponse.getSourceAsString();
        User user = JSON.parseObject(sourceAsString, User.class);
        return user;
    }

    /**
     * 根据文档id删除该文档
     * @param deleteUserVo
     * @return
     * @throws IOException
     */
    public DocWriteResponse.Result deleteDoc(DeleteUserVo deleteUserVo) throws IOException {
        DeleteRequest deleteRequest = new DeleteRequest();
        deleteRequest.index(deleteUserVo.getIndexName()).id(deleteUserVo.getId());
        DeleteResponse delete = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
        DocWriteResponse.Result result = delete.getResult();
        return result;
    }

    /**
     * 批量新增文档
     * @param insertBatchUsersVo
     * @return
     * @throws IOException
     */
    public List<String> insertBatch(InsertBatchUsersVo insertBatchUsersVo) throws IOException {
        BulkRequest bulkRequest = new BulkRequest();
        for (User user: insertBatchUsersVo.getUsers()) {
            IndexRequest indexRequest = new IndexRequest();
            indexRequest
                    .index(insertBatchUsersVo.getIndexName())
                    .id(String.valueOf(user.getUserId()))
                    .source(JSON.toJSONString(user),XContentType.JSON);
            bulkRequest.add(indexRequest);
        }
        BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
        //批量插入到文档id
        BulkItemResponse[] items = bulk.getItems();
        List<String> ids = new ArrayList<>();
        for (BulkItemResponse bulkItemResponse : items) {
            log.info("=====>>> "+bulkItemResponse);
            String id = bulkItemResponse.getResponse().getId();
            log.info("============  id: "+id);
            ids.add(id);
        }
        return ids;
    }

    /**
     * 批量删除文档
     * @param deleteBatchUsersVo
     * @return
     * @throws IOException
     */
    public Long deleteBatch(DeleteBatchUsersVo deleteBatchUsersVo) throws IOException {
        BulkRequest bulkRequest = new BulkRequest();
        for (Integer id : deleteBatchUsersVo.getIds()) {
            DeleteRequest deleteRequest = new DeleteRequest();
            deleteRequest.index(deleteBatchUsersVo.getIndexName()).id(String.valueOf(id));
            bulkRequest.add(deleteRequest);
        }
        BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
        TimeValue took = bulk.getTook();
        return took.getMillis();
    }

    /**
     * 全量查询文档信息
     * @param searchUserVo
     * @return
     * @throws IOException
     */
    public SearchUserResultVo searchAll(SearchUserVo searchUserVo) throws IOException {

        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices(searchUserVo.getIndexName());

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        String[] includes = {};
        String[] excludes = {"mobile"};
        searchSourceBuilder
                //上限1000条，
                //接触上限：PUT _all/_settings
                //	{
                //	"index.max_result_window":200000
                //	}
                .query(QueryBuilders.matchAllQuery()).size(10001).trackTotalHits(true);
                //分页
                //.from(0)
                //.size(4)
                //.sort("age", SortOrder.DESC)
                //排除展示mobile字段
                //.fetchSource(includes,excludes);
        //默认只展示10条，trackTotalHits为true则展示所有
        //.trackTotalHits(true)
        searchSourceBuilder.trackTotalHits(true);
        searchRequest.source(searchSourceBuilder);

        return getResult(searchRequest);
    }

    /**
     * 条件查询文档
     * @param searchUserVo
     * @return
     * @throws IOException
     */
    public SearchUserResultVo termQuery(SearchUserVo searchUserVo) throws IOException {
        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices(searchUserVo.getIndexName());
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchRequest.source(searchSourceBuilder.query(QueryBuilders.termQuery("userName", searchUserVo.getName())).size(100));

        return getResult(searchRequest);
    }

    /**
     * 查找年龄为20或21岁的 且 用户名为 叶俊辰或yyy的
     * @param multiConditionVo
     * @return
     * @throws IOException
     */
    public SearchUserResultVo multiCondition(MultiConditionVo multiConditionVo) throws IOException {

        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices(multiConditionVo.getIndexName());
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        BoolQueryBuilder ageShould = QueryBuilders.boolQuery();
        BoolQueryBuilder nameShould = QueryBuilders.boolQuery();
        ageShould.should(QueryBuilders.matchQuery("age",multiConditionVo.getOneAge()));
        ageShould.should(QueryBuilders.matchQuery("age",multiConditionVo.getTwoAge()));
        nameShould.should(QueryBuilders.matchQuery("userName",multiConditionVo.getUserNameOne()));
        nameShould.should(QueryBuilders.matchQuery("userName",multiConditionVo.getUserNameTwo()));
        boolQueryBuilder.must(ageShould).must(nameShould);

        searchSourceBuilder.query(boolQueryBuilder);
        searchRequest.source(searchSourceBuilder);
        return getResult(searchRequest);
    }

    /**
     * 范围查询
     * @param rangeSearchVo
     * @return
     * @throws IOException
     */
    public SearchUserResultVo rangeSearch(RangeSearchVo rangeSearchVo) throws IOException {
        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices(rangeSearchVo.getIndexName());

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("age");
        //>=
        rangeQueryBuilder.gte(20);
        //<=
        rangeQueryBuilder.lte(25);

        searchSourceBuilder.query(rangeQueryBuilder);
        searchRequest.source(searchSourceBuilder);

        return getResult(searchRequest);
    }

    /**
     * 模糊查询
     * @param fuzzyQueryVo
     * @return
     * @throws IOException
     */
    public SearchUserResultVo fuzzyQuery(FuzzyQueryVo fuzzyQueryVo) throws IOException {

        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices(fuzzyQueryVo.getIndexName());
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        FuzzyQueryBuilder fuzzyQueryBuilder = QueryBuilders.fuzzyQuery("userName", fuzzyQueryVo.getSearchName()).fuzziness(Fuzziness.ONE);

        searchSourceBuilder.query(fuzzyQueryBuilder);
        searchRequest.source(searchSourceBuilder);

        return getResult(searchRequest);
    }


    /**
     * 高亮查询
     * @param highlightQueryVo
     * @return
     * @throws IOException
     */
    public SearchUserResultVo highlight(HighlightQueryVo highlightQueryVo) throws IOException {

        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices(highlightQueryVo.getIndexName());
        SearchSourceBuilder builder = new SearchSourceBuilder();
        MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("userName", highlightQueryVo.getKeyword());

        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.requireFieldMatch(true);
        highlightBuilder.field("userName");
        highlightBuilder.preTags("<font color='red'>");
        highlightBuilder.postTags("</font>");
        builder.highlighter(highlightBuilder);

        builder.query(matchQueryBuilder).size(100);

        searchRequest.source(builder);

        SearchResponse searchResponse = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);
        System.out.println("====>>>   "+searchResponse);
        long value = searchResponse.getHits().getTotalHits().value;
        SearchHit[] hits = searchResponse.getHits().getHits();
        List<User> users = new ArrayList<>();
        for (SearchHit searchHit : hits) {
            log.info(searchHit.getSourceAsString());

            Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
            HighlightField highlightUserName = highlightFields.get("userName");
            String newUserName = "";
            if (null != highlightUserName){
                Text[] fragments = highlightUserName.getFragments();
                for (Text text : fragments) {
                    newUserName += text;
                }
            }

            String sourceAsString = searchHit.getSourceAsString();
            User user = JSON.parseObject(sourceAsString, User.class);
            user.setUserName(newUserName);
            users.add(user);
        }
        SearchUserResultVo searchUserResultVo = new SearchUserResultVo();
        searchUserResultVo.setUsers(users);
        searchUserResultVo.setTotal(value);
        return searchUserResultVo;
    }

    public SearchUserResultVo getResult(SearchRequest searchRequest) throws IOException {
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        long value = searchResponse.getHits().getTotalHits().value;
        SearchHit[] hits = searchResponse.getHits().getHits();
        List<User> users = new ArrayList<>();
        for (SearchHit searchHit : hits) {
            log.info(searchHit.getSourceAsString());
            String sourceAsString = searchHit.getSourceAsString();
            User user = JSON.parseObject(sourceAsString, User.class);
            users.add(user);
        }
        SearchUserResultVo searchUserResultVo = new SearchUserResultVo();
        searchUserResultVo.setTotal(value);
        searchUserResultVo.setUsers(users);
        return searchUserResultVo;
    }
}
